/**
 * 
 */
package com.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;



/**
 * 简历加密工具类
 * <一句话功能简述>
 * <功能详细描述>
 * 
 * @author  E3
 * @version  [版本号, 2009-3-25]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class EncryptorTool
{
    private static final String AES = "AES";
    //JYScqmygysdssztwmydtsgx_00该密码非常重要，且不可以随意改动。
    private static final String password = "JYScqmygysdssztwmydtsgx_00";

    private EncryptorTool(){}
    
    private static Cipher init(int mode) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
    {

        Security.addProvider(new sun.security.provider.Sun());
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG",new sun.security.provider.Sun());
        sr.setSeed(password.getBytes());
        KeyGenerator kgen = KeyGenerator.getInstance(AES);
        kgen.init(128, sr);
        SecretKey skey = kgen.generateKey();
        byte[] raw = skey.getEncoded();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
        Cipher cipher = Cipher.getInstance(AES);
        
        cipher.init(mode, skeySpec);
        return cipher;
    }

    private static void encrypt(Cipher cipher,InputStream fis, OutputStream fos) throws Exception
    {
        try
        {
            if (fis == null || fos == null)
            {
                throw new Exception("encrypt fail");
            }
            int blockSize = cipher.getBlockSize();
            byte[] buffer = new byte[blockSize];
            byte[] bytes;

            int i;
            for (i = fis.read(buffer); i == blockSize; i = fis.read(buffer))
            {
                bytes = cipher.update(buffer);
                fos.write(bytes);
            }
            if (i == -1)
            {
                bytes = cipher.doFinal();
            }
            else if (i == 0)
            {
                bytes = cipher.doFinal(new byte[0]);
            }
            else
            {
                bytes = cipher.doFinal(buffer, 0, i);
            }
            fos.write(bytes);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            fos.close();
            fis.close();
        }
    }

    /**
     * 加密FileInputStream fis，通过FileOutputStream fos输出
     * @param fis FileInputStream 文件输入流
     * @param fos FileOutputStream 文件输出流
     * @throws Exception
     */
    public static void encryptFileInputStrean(InputStream fis, OutputStream fos) throws Exception
    {
        Cipher cipher = init(Cipher.ENCRYPT_MODE);

        encrypt(cipher,fis, fos);
    }

    /**
     * 解密FileInputStream fis，通过FileOutputStream fos输出
     * @param fis FileInputStream 文件输入流
     * @param fos FileOutputStream 文件输出流
     * @throws Exception
     */
    public static void decryptFileInputStrean(FileInputStream fis, FileOutputStream fos) throws Exception
    {
        Cipher cipher =init(Cipher.DECRYPT_MODE);

        encrypt(cipher,fis, fos);
    }

    /**
     * 加密File src，输出为File dest
     * @param src File src 要加密的文件
     * @param dest File dest 加密后的文件
     * @throws Exception
     */
    public static void encryptFile(File src, File dest) throws Exception
    {

        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);

        encryptFileInputStrean(fis, fos);
    }

    /**
     * 加密File src，输出为File dest
     * @param src File src 要加密的文件
     * @param dest File dest 加密后的文件
     * @throws Exception
     */
    public static void decryptFile(File src, File dest) throws Exception
    {

        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);

        decryptFileInputStrean(fis, fos);
    }

    /**
     * 加密String srcPath，输出为File dest
     * @param src File src 要加密的文件
     * @param dest File dest 加密后的文件
     * @throws Exception
     */
    public static void encryptFileByPath(String srcPath, String destPath) throws Exception
    {
        if (srcPath == null || "".equals(srcPath) || destPath == null || "".equals(destPath))
        {
            throw new Exception("encrypt fail");
        }
        File src = new File(srcPath);
        if (!src.isFile())
        {
            throw new Exception("encrypt fail");
        }
        File dest = new File(destPath);
        if (!dest.isFile())
        {
            throw new Exception("encrypt fail");
        }
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);

        encryptFileInputStrean(fis, fos);
    }

    /**
     * 解密String srcPath，输出为File dest
     * @param src File src 要加密的文件
     * @param dest File dest 加密后的文件
     * @throws Exception
     */
    public static void decryptFileByPath(String srcPath, String destPath) throws Exception
    {
        if (srcPath == null || "".equals(srcPath) || destPath == null || "".equals(destPath))
        {
            throw new Exception("encrypt fail");
        }
        File src = new File(srcPath);
        if (!src.isFile())
        {
            throw new Exception("encrypt fail");
        }
        File dest = new File(destPath);
        if (!dest.isFile())
        {
            throw new Exception("encrypt fail");
        }
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);

        decryptFileInputStrean(fis, fos);
    }

    /**
     * 加密字符串
     * <功能详细描述>
     * @param param
     * @return
     * @throws Exception [参数说明]
     * 
     * @return String [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @SuppressWarnings("static-access")
    public static String encryptString(String param)throws Exception
    {
        if(param==null||"".equals(param))
        {
            return param;
        }
        Cipher cipher =init(Cipher.ENCRYPT_MODE);
        
       byte[]    bytes = cipher.doFinal(param.getBytes());

        return byte2hex(bytes);
    }
    
    public static String byte2hex ( byte[] b )
    {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++)
        {
            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (stmp.length() == 1)
            {
                hs = hs + "0" + stmp;
            }
            else
            {
                hs = hs + stmp;
            }
        }
        return hs.toUpperCase();
    }

    
    /**
     * 解密字符串
     * <功能详细描述>
     * @param param
     * @return
     * @throws Exception [参数说明]
     * 
     * @return String [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @SuppressWarnings("static-access")
    public static String decryptString(String param)throws Exception
    {
        if(param==null||"".equals(param))
        {
            return "";
        }
        Cipher cipher =init(Cipher.DECRYPT_MODE);
        byte[] bytes = cipher.doFinal(hex2byte(param));
        return new String(bytes,"utf-8");
    }
    public static byte[] hex2byte(String strhex) {
        if (strhex == null) {
            return null;
        }
        int l = strhex.length();
        if (l % 2 == 1) {
            return null;
        }
        byte[] b = new byte[l / 2];
        for (int i = 0; i != l / 2; i++) {
            b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2), 16);
        }
        return b;
    }
    
    /**
     * 下载简历
     * <功能详细描述>
     * @param src  要下载的文件
     * @param response HttpServletResponse
     * @param name 文件名，如果不传则从取src的名字
     * @throws Exception [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public static void downEncryptFile(File src, HttpServletResponse response,String name) throws Exception
    {
        if (!src.exists())
        {
            throw new Exception("download file fail");
        }
        InputStream is = null;
        OutputStream os = null;
        try
        {
            // 取得文件名。
            String filename = name==null||"".equals(name)?src.getName():name;
            //初始化AES解密实例
            Cipher cipher =init(Cipher.DECRYPT_MODE);
            is =new FileInputStream(src);
            // 清空response
            response.reset();
            // 设置response的Header
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes()));
            // 设置内容长度
            response.setContentLength((int)src.length());
            //设置内容类型
            //response.setContentType("application/msword");
            os = response.getOutputStream();
            int blockSize = cipher.getBlockSize();
            byte[] buffer = new byte[blockSize];
            byte[] bytes;

            int i;
            for (i = is.read(buffer); i == blockSize; i = is.read(buffer))
            {
                bytes = cipher.update(buffer);
                os.write(bytes);
            }
            if (i == -1)
            {
                bytes = cipher.doFinal();
            }
            else if (i == 0)
            {
                bytes = cipher.doFinal(new byte[0]);
            }
            else
            {
                bytes = cipher.doFinal(buffer, 0, i);
            }
            os.write(bytes);
            os.write(buffer);

        }
        catch (IOException ex)
        {
//            ex.printStackTrace();
        }finally
        {
            os.flush();
            os.close();
            is.close();
            
        }

    }

    /**
     * 加密文件加下的文件（没有处理src统计的文件或目录），并在src相同的位置备份一个*_bak.doc
     * @param src  要加密的文件或文件加
     * @throws Exception
     */
    public static  void bak(File src)throws Exception
    {
        if(src.isDirectory())
        {
            for(File f :src.listFiles())
            {
                bak(f);
            }
        }else
        {
            //拷贝一份，文件名为*_bak.doc
            File file = new File(src.getPath().replace(src.getName().substring(0,src.getName().length()-4), src.getName().substring(0,src.getName().length()-4)+"_bak"));
            src.renameTo(file);
            
            //加密
            encryptFile(file, src);
        }
    }
    
    /**
     * bak()方法的逆方法，恢复成执行bak()的文件
     * @param src
     * @throws Exception
     */
    public static  void debak(File src)throws Exception
    {
        if(src.isDirectory())
        {
            for(File f :src.listFiles())
            {
                debak(f);
            }
        }else
        {
            if(src.getName().indexOf("_bak")!=-1)
            {
                //删除_bak文件
                src.delete();
            }else
            {
                String path = src.getPath();
                //拷贝一份，文件名为*_tempbak.doc
                File file = new File(src.getPath().replace(src.getName().substring(0,src.getName().length()-4), src.getName().substring(0,src.getName().length()-4)+"_tempbak"));
                src.renameTo(file);
                
                //解密
                decryptFile(file, new File(path));
                file.delete();
            }
        }
    }

	/**
	 * 将文件名中的汉字转为UTF8编码的串,以便下载时能正确显示另存的文件名.
	 * EncryptorTool.downEncryptFile(new File(path), response, toUtf8String(resumeName));
	 * @param s 原文件名
	 * @return 重新编码后的文件名
	 */
	@SuppressWarnings("unused")
	private static String toUtf8String(String s)
	{
	    StringBuffer sb = new StringBuffer();
	    for (int i = 0; i < s.length(); i++)
	    {
	        char c = s.charAt(i);
	        if (c >= 0 && c <= 255)
	        {
	            sb.append(c);
	        }
	        else
	        {
	            byte[] b;
	            try
	            {
	                b = Character.toString(c).getBytes("utf-8");
	            }
	            catch (Exception ex)
	            {
	                System.out.println(ex);
	                b = new byte[0];
	            }
	            for (int j = 0; j < b.length; j++)
	            {
	                int k = b[j];
	                if (k < 0)
	                    k += 256;
	                sb.append("%" + Integer.toHexString(k).toUpperCase());
	            }
	        }
	    }
	    return sb.toString();
	}
}
//文件加密完成